package com.home.common.utils.encryption;

import com.home.common.constants.encryption.DesConstant;
import com.home.common.utils.string.StringUtils;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.Key;

/**
 * Description: DES对称加解密算法
 * Created by 王大宸 on 2020-09-25 9:36
 * Created with IntelliJ IDEA.
 */
public class DcEncryptionCode {
    private static final Logger logger = LoggerFactory.getLogger(DcEncryptionCode.class);

    //创建Base64对象,用于加密和解密;
    private final static Base64 base64 = new Base64();

    /***
     * 功能说明：加密
     *
     * @author 王大宸
     * @date 2020/9/25 15:14
     * @param str 需要加密的字符串
     * @return java.lang.String
     */
    public static String encrypt(String str) {
        return encrypt(str, DesConstant.PASSWORD_KEY);
    }

    /***
     * 功能说明：解密
     *
     * @author 王大宸
     * @date 2020/9/25 15:16
     * @param str 需要解密的字符串
     * @return java.lang.String
     */
    public static String decrypt(String str) {
        return decrypt(str, DesConstant.PASSWORD_KEY);
    }

    /***
     * 功能说明：先用des算法加密，在用base64编码后返回
     *
     * @author 王大宸
     * @date 2020/9/25 15:16
     * @param str    需要加密的字符串
     * @param sKey   加密密钥
     * @return java.lang.String
     */
    private static String encrypt(String str, String sKey) {
        // 声明加密后的结果字符串变量
        String result = str;
        if (StringUtils.isEmpty(str)) {
            logger.error("加密字符串不能为空...");
            throw new RuntimeException("加密字符串不能为空...");
        }
        if (str != null && str.length() > 0 && sKey != null && sKey.length() >= 8) {
            try {
                //调用DES 加密数组的 encrypt方法，返回加密后的byte数组;
                byte[] encodeByte = encryptBasedDes(str.getBytes(DesConstant.ENCODING), sKey);
                // 调用base64的编码方法，返回加密后的字符串;
                result = base64.encodeToString(encodeByte).trim();
            } catch (Exception e) {
                logger.error("加密密钥不能为空且不能小于8位。", e);
            }
        } else {
            logger.error("加密密钥不能为空且不能小于8位。");
            throw new RuntimeException("加密密钥不能为空且不能小于8位。");
        }
        return result;
    }

    /***
     * 功能说明：先用base64解码，再用des解密后返回
     *
     * @author 王大宸
     * @date 2020/9/25 15:15
     * @param str    需要解密的字符串
     * @param sKey   解密密钥
     * @return java.lang.String
     */
    private static String decrypt(String str, String sKey) {
        String result = str;
        if (str != null && str.length() > 0 && sKey != null && sKey.length() >= 8) {
            try {
                // 用base64进行编码，返回byte数组
                byte[] encodeByte = base64.decode(str);
                // 调用DES 解密数组的decrypte方法，返回解密后的数组;
                byte[] decoder = decryptBasedDes(encodeByte, sKey);
                // 对解密后的数组转化成字符串
                result = new String(decoder, DesConstant.ENCODING).trim();
            } catch (Exception e) {
                logger.error("用base64解码，再用des解密后返回失败: ", e);
                result = "";
            }
        }
        return result;
    }

    /***
     * 功能说明：先用DES算法对byte[]数组加密
     *
     * @author 王大宸
     * @date 2020/9/25 15:15
     * @param byteSource  需要加密的数据
     * @param sKey        加密密钥
     * @return byte[]
     */
    private static byte[] encryptBasedDes(byte[] byteSource, String sKey) {
        try {
            // 声明加密模式;
            int type = Cipher.ENCRYPT_MODE;
            return secretKeyFactory(type, byteSource, sKey);
        } catch (Exception e) {
            logger.error("用DES算法对byte[]数组加密失败: ", e);
            throw new RuntimeException("用DES算法对byte[]数组加密失败");
        }
    }

    /***
     * 功能说明：先用DES算法对byte[]数组解密
     *
     * @author 王大宸
     * @date 2020/9/25 15:15
     * @param byteSource  需要解密的数据
     * @param sKey        解密密钥
     * @return byte[]
     */
    private static byte[] decryptBasedDes(byte[] byteSource, String sKey) {
        try {
            // 声明解密模式;
            int type = Cipher.DECRYPT_MODE;
            return secretKeyFactory(type, byteSource, sKey);
        } catch (Exception e) {
            logger.error("DES算法对byte[]数组解密失败: ", e);
            throw new RuntimeException("用DES算法对byte[]数组解密失败");
        }
    }

    /***
     * 功能说明：加密工厂
     *
     * @author 王大宸
     * @date 2020/9/25 16:48
     * @param type          加密/解密模式
     * @param byteSource    加密/解密数据
     * @param sKey          加密/解密密钥
     * @return byte[]
     */
    private static byte[] secretKeyFactory(int type, byte[] byteSource, String sKey) {
        try {
            // 创建密码工厂对象;
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DesConstant.ENC_DES);
            // 把字符串格式的密钥转成字节数组;
            byte[] keyData = sKey.getBytes();
            // 以密钥数组为参数，创建密码规则
            DESKeySpec keySpec = new DESKeySpec(keyData);
            // 以密码规则为参数，用密码工厂生成密码
            Key key = keyFactory.generateSecret(keySpec);
            // 创建密码对象
            Cipher cipher = Cipher.getInstance(DesConstant.ENC_DES);
            // 以加密模式和密码为参数对密码对象 进行初始化
            cipher.init(type, key);
            // 完成最终加密/解密
            return cipher.doFinal(byteSource);
        } catch (Exception e) {
            logger.error("加密工厂创建失败: ", e);
            throw new RuntimeException("加密工厂创建失败");
        }
    }

    /***
     * 功能说明：测试加密算法
     *
     * @author 王大宸
     * @date 2020/9/25 15:14
     * @param args
     * @return void
     */
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/dc_web?useUnicode=true&characterEncoding=UTF8&createDatabaseIfNotExist=true&useUnicode=true&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8&allowPublicKeyRetrieval=true";
        String username = "root";
        String password = "root";
        String urlStr = encrypt(url);
        String usernameStr = encrypt(username);
        String passwordStr = encrypt(password);
        System.out.println("url: " + urlStr);
        System.out.println("username: " + usernameStr);
        System.out.println("password: " + passwordStr);
    }


}
